package com.example.repository;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.data.repository.support.PageableExecutionUtils.TotalSupplier;
import org.springframework.util.StringUtils;

import com.example.bean.OrderCondition;
import com.example.model.Order;
import com.example.model.OrderItem;
import com.example.model.Product;

public class OrderRepositoryImpl implements OrderRepositoryCustm {

    @PersistenceContext
    EntityManager em;

    @Override
    public Order findByOrderNum(String orderNum) {

        Query q = em.createQuery("select o from Order o where o.orderNum=:orderNum");
        q.setParameter("orderNum", orderNum);

        if (q.getSingleResult() == null) {
            return null;
        }

        return (Order)q.getSingleResult();
    }

    @Override
    @SuppressWarnings("unchecked")
    public Page<Order> findByCriteria(OrderCondition orderCondition, Pageable pageable) {

        // 检索条件
        Specification<Order> spec = new Specification<Order>() {

            @Override
            public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicate = new ArrayList<>();

                // 结合条件
                SetJoin<Order, OrderItem> joinOrderItem = null;
                Join<OrderItem, Product> joinProduct = null;
                if (orderCondition.getBuyNum() != 0) {
                    joinOrderItem = root.joinSet("orderItems", JoinType.INNER);
                }
                if (!StringUtils.isEmpty(orderCondition.getProductName())) {
                    joinProduct = joinOrderItem.join("product", JoinType.INNER);
                }

                // Where 条件
                if (!StringUtils.isEmpty(orderCondition.getOrderNum())) {
                    predicate.add(cb.equal(root.get("orderNum").as(String.class), orderCondition.getOrderNum()));
                }
                if (orderCondition.getBuyNum() != 0) {
                    predicate.add(cb.equal(joinOrderItem.get("buyNum").as(Integer.class), orderCondition.getBuyNum()));
                }
                if (!StringUtils.isEmpty(orderCondition.getProductName())) {
                    predicate.add(cb.equal(joinProduct.get("name").as(String.class), orderCondition.getProductName()));
                }

                return cb.and(predicate.toArray(new Predicate[]{}));
            }
        };

        CriteriaBuilder cb= em.getCriteriaBuilder();
        Class<Order> domainClass = Order.class;

        // 件数检索
        CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
        Root<Order> rootForCount = cqCount.from(domainClass);
        cqCount.select(cb.count(rootForCount));
        Query qForCount = em.createQuery(cqCount.where(spec.toPredicate(rootForCount, cqCount, cb)));
        final long count = (long)qForCount.getSingleResult();

        // 如果记录不存在
        if (count == 0) {
            return null;
        }

        // 数据检索
        CriteriaQuery<Order> cq = cb.createQuery(domainClass);
        Root<Order> root = cq.from(domainClass);
        cq.orderBy(QueryUtils.toOrders(pageable.getSort(), root, cb)); // 排序
        Query q = em.createQuery(cq.where(spec.toPredicate(root, cq, cb)));
        q.setFirstResult(pageable.getOffset());  // 数据偏移量
        q.setMaxResults(pageable.getPageSize()); // 一页表示件数
        return PageableExecutionUtils.getPage(q.getResultList(), pageable, new TotalSupplier() {
            @Override
            public long get() {
                return count;
            }
        });
    }
}
